//////////////////////////////////////////////////////////////////
//                                                              //
//  Wishbone Arbiter                                            //
//                                                              //
//  This file is part of the Amber project                      //
//  http://www.opencores.org/project,amber                      //
//                                                              //
//  Description                                                 //
//  Arbitrates between 2 wishbone masters and 8 wishbone        //
//  slave modules. Master 0 is given priority over Master 1.    //
//                                                              //
//  Author(s):                                                  //
//      - Conor Santifort, csantifort.amber@gmail.com           //
//                                                              //
//////////////////////////////////////////////////////////////////
//                                                              //
// Copyright (C) 2010 Authors and OPENCORES.ORG                 //
//                                                              //
// This source file may be used and distributed without         //
// restriction provided that this copyright statement is not    //
// removed from the file and that any derivative work contains  //
// the original copyright notice and the associated disclaimer. //
//                                                              //
// This source file is free software; you can redistribute it   //
// and/or modify it under the terms of the GNU Lesser General   //
// Public License as published by the Free Software Foundation; //
// either version 2.1 of the License, or (at your option) any   //
// later version.                                               //
//                                                              //
// This source is distributed in the hope that it will be       //
// useful, but WITHOUT ANY WARRANTY; without even the implied   //
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
// PURPOSE.  See the GNU Lesser General Public License for more //
// details.                                                     //
//                                                              //
// You should have received a copy of the GNU Lesser General    //
// Public License along with this source; if not, download it   //
// from http://www.opencores.org/lgpl.shtml                     //
//                                                              //
//////////////////////////////////////////////////////////////////

module wishbone_arbiter #(
parameter WB_DWIDTH  = 32,
parameter WB_SWIDTH  = 4,

parameter M0AWIDTH = 30,
parameter M1AWIDTH = 30,
parameter S0AWIDTH = 30,
parameter S1AWIDTH = 30,
parameter S2AWIDTH = 30,
parameter S3AWIDTH = 30,
parameter S4AWIDTH = 30,
parameter S5AWIDTH = 30,
parameter S6AWIDTH = 30,
parameter S7AWIDTH = 30
)(

input                       i_wb_clk,     // WISHBONE clock

// WISHBONE master 0
input       [M0AWIDTH+1:2]  i_m0_wb_adr,
input       [WB_SWIDTH-1:0] i_m0_wb_sel,
input                       i_m0_wb_we,
output      [WB_DWIDTH-1:0] o_m0_wb_dat,
input       [WB_DWIDTH-1:0] i_m0_wb_dat,
input                       i_m0_wb_cyc,
input                       i_m0_wb_stb,
output                      o_m0_wb_ack,
//output                      o_m0_wb_err,


// WISHBONE master 1
input       [M1AWIDTH+1:2]  i_m1_wb_adr,
input       [WB_SWIDTH-1:0] i_m1_wb_sel,
input                       i_m1_wb_we,
output      [WB_DWIDTH-1:0] o_m1_wb_dat,
input       [WB_DWIDTH-1:0] i_m1_wb_dat,
input                       i_m1_wb_cyc,
input                       i_m1_wb_stb,
output                      o_m1_wb_ack,
//output                      o_m1_wb_err,


// WISHBONE slave 0
output      [S0AWIDTH+1:2]  o_s0_wb_adr,
output      [WB_SWIDTH-1:0] o_s0_wb_sel,
output                      o_s0_wb_we,
input       [WB_DWIDTH-1:0] i_s0_wb_dat,
output      [WB_DWIDTH-1:0] o_s0_wb_dat,
output                      o_s0_wb_cyc,
output                      o_s0_wb_stb,
input                       i_s0_wb_ack,
//input                       i_s0_wb_err,


// WISHBONE slave 1
output      [S1AWIDTH+1:2]  o_s1_wb_adr,
output      [WB_SWIDTH-1:0] o_s1_wb_sel,
output                      o_s1_wb_we,
input       [WB_DWIDTH-1:0] i_s1_wb_dat,
output      [WB_DWIDTH-1:0] o_s1_wb_dat,
output                      o_s1_wb_cyc,
output                      o_s1_wb_stb,
input                       i_s1_wb_ack,
//input                       i_s1_wb_err,


// WISHBONE slave 2
output      [S2AWIDTH+1:2]  o_s2_wb_adr,
output      [WB_SWIDTH-1:0] o_s2_wb_sel,
output                      o_s2_wb_we,
input       [WB_DWIDTH-1:0] i_s2_wb_dat,
output      [WB_DWIDTH-1:0] o_s2_wb_dat,
output                      o_s2_wb_cyc,
output                      o_s2_wb_stb,
input                       i_s2_wb_ack,
//input                       i_s2_wb_err,


// WISHBONE slave 3
output      [S3AWIDTH+1:2]  o_s3_wb_adr,
output      [WB_SWIDTH-1:0] o_s3_wb_sel,
output                      o_s3_wb_we,
input       [WB_DWIDTH-1:0] i_s3_wb_dat,
output      [WB_DWIDTH-1:0] o_s3_wb_dat,
output                      o_s3_wb_cyc,
output                      o_s3_wb_stb,
input                       i_s3_wb_ack,
//input                       i_s3_wb_err,


// WISHBONE slave 4
output      [S4AWIDTH+1:2]  o_s4_wb_adr,
output      [WB_SWIDTH-1:0] o_s4_wb_sel,
output                      o_s4_wb_we,
input       [WB_DWIDTH-1:0] i_s4_wb_dat,
output      [WB_DWIDTH-1:0] o_s4_wb_dat,
output                      o_s4_wb_cyc,
output                      o_s4_wb_stb,
input                       i_s4_wb_ack,
//input                       i_s4_wb_err,


// WISHBONE slave 5
output      [S5AWIDTH+1:2]  o_s5_wb_adr,
output      [WB_SWIDTH-1:0] o_s5_wb_sel,
output                      o_s5_wb_we,
input       [WB_DWIDTH-1:0] i_s5_wb_dat,
output      [WB_DWIDTH-1:0] o_s5_wb_dat,
output                      o_s5_wb_cyc,
output                      o_s5_wb_stb,
input                       i_s5_wb_ack,
//input                       i_s5_wb_err,


// WISHBONE slave 6
output      [S6AWIDTH+1:2]  o_s6_wb_adr,
output      [WB_SWIDTH-1:0] o_s6_wb_sel,
output                      o_s6_wb_we,
input       [WB_DWIDTH-1:0] i_s6_wb_dat,
output      [WB_DWIDTH-1:0] o_s6_wb_dat,
output                      o_s6_wb_cyc,
output                      o_s6_wb_stb,
input                       i_s6_wb_ack,
//input                       i_s6_wb_err,


 // WISHBONE slave 7
output      [S7AWIDTH+1:2]  o_s7_wb_adr,
output      [WB_SWIDTH-1:0] o_s7_wb_sel,
output                      o_s7_wb_we,
input       [WB_DWIDTH-1:0] i_s7_wb_dat,
output      [WB_DWIDTH-1:0] o_s7_wb_dat,
output                      o_s7_wb_cyc,
output                      o_s7_wb_stb,
input                       i_s7_wb_ack
//input                       i_s7_wb_err
);

reg         m0_wb_hold_r = 'd0;
reg         m1_wb_hold_r = 'd0;
wire        current_master;
reg         current_master_r = 'd0;
wire        next_master;
wire        select_master;

wire [31:2]             master_adr;
wire [WB_SWIDTH-1:0]    master_sel;
wire                    master_we;
wire [WB_DWIDTH-1:0]    master_wdat;
wire                    master_cyc;
wire                    master_stb;
wire [WB_DWIDTH-1:0]    master_rdat;
wire                    master_ack;
//wire                    master_err;


// Arbitrate between m0 and m1. m0 always gets priority
assign next_master    = i_m0_wb_cyc ? 1'd0 : 1'd1;

// Use cyc signal for arbitration so block accesses are not split up
// assign m0_in_cycle    = m0_wb_hold_r && !master_ack;
// assign m1_in_cycle    = m1_wb_hold_r && !master_ack;

// only select a new bus master when the current bus master
// daccess ends
assign select_master  = current_master_r ? !m1_wb_hold_r : !m0_wb_hold_r;
assign current_master = select_master ? next_master : current_master_r;


always @( posedge i_wb_clk ) begin
  current_master_r    <= current_master;
  m0_wb_hold_r        <= i_m0_wb_stb && !o_m0_wb_ack;
  m1_wb_hold_r        <= i_m1_wb_stb && !o_m1_wb_ack;
end

// memory configuration

function in_s0; // Boot RAM
  input [31:2] a;
  begin
    in_s0 = {a[31:16], 16'b0} == 32'h00000000;
  end
endfunction

function in_s1; // UART
  input [31:2] a;
  begin
    in_s1 = {a[31:5], 5'b0} == 32'hfffe0020;
  end
endfunction

function in_s2; // Timer
  input [31:2] a;
  begin
    in_s2 = {a[31:3], 3'b0} == 32'hfffe0008;
  end
endfunction

function in_s3; // Ethernet MAC
  input [31:2] a;
  begin
    in_s3 = {a[31:12], 12'b0} == 32'hfffe1000;
  end
endfunction

function in_s4; // Pushbuttons
  input [31:2] a;
  begin
    in_s4 = {a[31:2], 2'b0} == 32'hfffe0010;
  end
endfunction

function in_s5; // DDR3 SDRAM
  input [31:2] a;
  begin
    // 1GB
    in_s5 = {a[31:30], 30'b0} == 32'h80000000;
  end
endfunction

function in_s6; // Simulation control register
  input [31:2] a;
  begin
    in_s6 = {a[31:3], 3'b0} == 32'hfffe0000;
  end
endfunction

function in_s7; // unused
  input [31:2] a;
  begin
    in_s7 = 1'b0;
  end
endfunction

wire s0_select = in_s0 ( master_adr ) ;
wire s1_select = in_s1 ( master_adr ) ;
wire s2_select = in_s2 ( master_adr ) ;
wire s3_select = in_s3 ( master_adr ) ;
wire s4_select = in_s4 ( master_adr ) ;
wire s5_select = in_s5 ( master_adr ) ;
wire s6_select = in_s6 ( master_adr ) ;
wire s7_select = in_s7 ( master_adr ) ;

reg s0_select_r;
reg s1_select_r;
reg s2_select_r;
reg s3_select_r;
reg s4_select_r;
reg s5_select_r;
reg s6_select_r;
reg s7_select_r;

always @(posedge i_wb_clk) begin
        s0_select_r <= s0_select;
        s1_select_r <= s1_select;
        s2_select_r <= s2_select;
        s3_select_r <= s3_select;
        s4_select_r <= s4_select;
        s5_select_r <= s5_select;
        s6_select_r <= s6_select;
        s7_select_r <= s7_select;
end

assign master_adr   = current_master ? i_m1_wb_adr : i_m0_wb_adr ;
assign master_sel   = current_master ? i_m1_wb_sel : i_m0_wb_sel ;
assign master_wdat  = current_master ? i_m1_wb_dat : i_m0_wb_dat ;
assign master_we    = current_master ? i_m1_wb_we  : i_m0_wb_we  ;
assign master_cyc   = current_master ? i_m1_wb_cyc : i_m0_wb_cyc ;
assign master_stb   = current_master ? i_m1_wb_stb : i_m0_wb_stb ;


// Slave 0 outputs
assign o_s0_wb_adr  = master_adr[S0AWIDTH+1:2];
assign o_s0_wb_dat  = master_wdat;
assign o_s0_wb_sel  = master_sel;
assign o_s0_wb_we   = master_we;
assign o_s0_wb_cyc  = s0_select & master_cyc;
assign o_s0_wb_stb  = s0_select & master_stb;

// Slave 1 outputs
assign o_s1_wb_adr  = master_adr[S1AWIDTH+1:2];
assign o_s1_wb_dat  = master_wdat;
assign o_s1_wb_sel  = master_sel;
assign o_s1_wb_we   = master_we;
assign o_s1_wb_cyc  = s1_select & master_cyc;
assign o_s1_wb_stb  = s1_select & master_stb;

// Slave 2 Outputs
assign o_s2_wb_adr  = master_adr[S2AWIDTH+1:2];
assign o_s2_wb_dat  = master_wdat;
assign o_s2_wb_sel  = master_sel;
assign o_s2_wb_we   = master_we;
assign o_s2_wb_cyc  = s2_select & master_cyc;
assign o_s2_wb_stb  = s2_select & master_stb;

// Slave 3 Outputs
assign o_s3_wb_adr  = master_adr[S3AWIDTH+1:2];
assign o_s3_wb_dat  = master_wdat;
assign o_s3_wb_sel  = master_sel;
assign o_s3_wb_we   = master_we;
assign o_s3_wb_cyc  = s3_select & master_cyc;
assign o_s3_wb_stb  = s3_select & master_stb;

// Slave 4 Outputs
assign o_s4_wb_adr  = master_adr[S4AWIDTH+1:2];
assign o_s4_wb_dat  = master_wdat;
assign o_s4_wb_sel  = master_sel;
assign o_s4_wb_we   = master_we;
assign o_s4_wb_cyc  = s4_select & master_cyc;
assign o_s4_wb_stb  = s4_select & master_stb;

// Slave 5 Outputs
assign o_s5_wb_adr  = master_adr[S5AWIDTH+1:2];
assign o_s5_wb_dat  = master_wdat;
assign o_s5_wb_sel  = master_sel;
assign o_s5_wb_we   = master_we;
assign o_s5_wb_cyc  = s5_select & master_cyc;
assign o_s5_wb_stb  = s5_select & master_stb;

// Slave 6 Outputs
assign o_s6_wb_adr  = master_adr[S6AWIDTH+1:2];
assign o_s6_wb_dat  = master_wdat;
assign o_s6_wb_sel  = master_sel;
assign o_s6_wb_we   = master_we;
assign o_s6_wb_cyc  = s6_select & master_cyc;
assign o_s6_wb_stb  = s6_select & master_stb;

// Slave 7 Outputs
assign o_s7_wb_adr  = master_adr[S7AWIDTH+1:2];
assign o_s7_wb_dat  = master_wdat;
assign o_s7_wb_sel  = master_sel;
assign o_s7_wb_we   = master_we;
assign o_s7_wb_cyc  = s7_select & master_cyc;
assign o_s7_wb_stb  = s7_select & master_stb;


// Master Outputs
assign master_rdat  = {32{s0_select_r}} & i_s0_wb_dat |
                      {32{s1_select_r}} & i_s1_wb_dat |
                      {32{s2_select_r}} & i_s2_wb_dat |
                      {32{s3_select_r}} & i_s3_wb_dat |
                      {32{s4_select_r}} & i_s4_wb_dat |
                      {32{s5_select_r}} & i_s5_wb_dat |
                      {32{s6_select_r}} & i_s6_wb_dat |
                      {32{s7_select_r}} & i_s7_wb_dat ;


assign master_ack   = i_s0_wb_ack |
                      i_s1_wb_ack |
                      i_s2_wb_ack |
                      i_s3_wb_ack |
                      i_s4_wb_ack |
                      i_s5_wb_ack |
                      i_s6_wb_ack |
                      i_s7_wb_ack ;

// Master 0 Outputs
assign o_m0_wb_dat = master_rdat;
assign o_m0_wb_ack = current_master ? 1'b0 : master_ack ;
//assign o_m0_wb_err  = current_master  ? 1'd0 : master_err ;

// Master 1 Outputs
assign o_m1_wb_dat = master_rdat;
assign o_m1_wb_ack = current_master ? master_ack : 1'b0 ;
//assign o_m1_wb_err  = current_master  ?  master_err : 1'd0 ;

endmodule
